{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# CLASSES AND OBJECT-ORIENTED PROGRAMMING"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 1 Object-oriented programming \n",
    "\n",
    "Object-oriented programming (OOP) is a programming paradigm based on the concept of **\"objects\"**, which can contain data and code:\n",
    "\n",
    "`data` in the form of  `attributes`, and \n",
    "\n",
    "`code`, in the form of `methods`\n",
    "\n",
    "* objects: data+code -> attribute +  method \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2 Classes\n",
    "\n",
    "We now turn to the object-oriented programming in Python: using `classes` to organize programs around `modules and data abstractions`\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### 2.1 The Simple Class\n",
    "\n",
    "In Python, one implements **abstractions** using **class**.\n",
    "\n",
    "Let's write a `Circle` class contain: \n",
    "\n",
    "* a attribute `radius`,`area`\n",
    "\n",
    "* a method `cal_area(`)\n",
    "\n",
    "**1 In C++**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting ./demo/src/circle.cpp\n"
     ]
    }
   ],
   "source": [
    "%%file ./demo/src/circle.cpp\n",
    "\n",
    "#include <iostream>\n",
    "#include <math.h>\n",
    "using namespace std;\n",
    "\n",
    "class TCircle\n",
    "// A Circle instance models a circle with a radius \n",
    "{\n",
    "  private:\n",
    "  \n",
    "  public: \n",
    "    float radius;\n",
    "    float area;\n",
    "    \n",
    "    TCircle(float fradius=1.0);\n",
    "    \n",
    "    void cal_area();\n",
    "};\n",
    "\n",
    "TCircle:: TCircle(float fradius)\n",
    "{\n",
    "    radius=fradius;   \n",
    "};\n",
    "\n",
    "void TCircle::cal_area()\n",
    "{\n",
    "    area=radius * radius * M_PI;\n",
    "};        \n",
    "\n",
    "int main() {\n",
    "   float radius=2.1;\n",
    "   float area;\n",
    "   TCircle c1(radius);\n",
    "   c1.cal_area(); \n",
    "   cout << \"The Circle: radius=\"<<c1.radius<<\"\\tarea=\"<<c1.area<<endl;\n",
    "   return 0;\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "!g++ -o ./demo/bin/circle ./demo/src/circle.cpp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The Circle: radius=2.1\tarea=13.8544\n"
     ]
    }
   ],
   "source": [
    "!.\\demo\\bin\\circle"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**2 In Python**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "from math import pi\n",
    " \n",
    "class Circle:   \n",
    "    \"\"\"A Circle instance models a circle with a radius\"\"\"\n",
    " \n",
    "    def __init__(self, radius=1.0):\n",
    "        \"\"\"Initializer with default radius of 1.0\"\"\"\n",
    "        self.radius = radius  # Create an instance variable radius\n",
    "\n",
    "    def cal_area(self):\n",
    "        \"\"\"the area of this Circle instance\"\"\"\n",
    "        self.area=self.radius * self.radius * pi"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "13.854423602330987"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "radius=2.1\n",
    "c1=Circle(radius)\n",
    "c1.cal_area()\n",
    "c1.area"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A class definition \n",
    "\n",
    "* 1 Creates an `object` of `class` type  \n",
    "\n",
    "* 2 Creates a set of `attributes` and `methods` that belong to the class\n",
    "\n",
    "####  1  Create an `object` of `class` type \n",
    "use the `class` keyword to define a new type class:`Circle`\n",
    "\n",
    "*  a subclass of `object`\n",
    "\n",
    "```python\n",
    "class Circle: \n",
    "    \"\"\"A Circle instance models a circle with a radius\"\"\" \n",
    "```    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'type'>\n"
     ]
    }
   ],
   "source": [
    "print(type(Circle))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2 Creates a set of `attributes` and `methods`\n",
    "\n",
    "An class contains \n",
    "\n",
    "*  **attributes**\n",
    "\n",
    "   * think of data as other objects that make up the class \n",
    "\n",
    "*  **methods**\n",
    "\n",
    "   * think of methods as functions that only work with this class\n",
    "   \n",
    "   * how to interact with the object\n",
    "\n",
    "All  attributes and methods are <b style=\"color:blue\">PUBLIC</b>\n",
    "\n",
    ">Python does not support access control.In other words, all attributes are \"public\" and are accessible by ALL.\n",
    "\n",
    "##### 2.1 attributes\n",
    "\n",
    "class attributes: <b style=\"color:blue\">Instance variable</b>\n",
    "\n",
    "```python\n",
    "    def __init__(self, radius=1.0):\n",
    "        \"\"\"Initializer with default radius of 1.0\"\"\"\n",
    "        self.radius = radius  # Create an instance variable radius\n",
    "        \n",
    "    def cal_area(self):\n",
    "        \"\"\"the area of this Circle instance\"\"\"\n",
    "        self.area=self.radius * self.radius * pi     \n",
    "```\n",
    "\n",
    "```python\n",
    "self.radius\n",
    "self.area\n",
    "```\n",
    "* Every <b style=\"color:blue\">Instance variable</b> begin with <b style=\"color:blue\">self.</b> \n",
    "\n",
    "* One Instance variable can be **defined in any method** `as you need`,begined with <b style=\"color:blue\">self.</b>:\n",
    "\n",
    "  * <b style=\"color:blue\">self</b>: the instance  of  the class\n",
    "\n",
    "##### 2.2 Methods\n",
    "\n",
    "```python\n",
    "  def __init__(self, radius=1.0):\n",
    " \n",
    "  def cal_area(self):\n",
    "```\n",
    "Every method uses <b style=\"color:blue\">self</b>  as the name of <b style=\"color:blue\">the first argument</b> of all methods\n",
    "\n",
    "* Python always passes the object as the `first` argument.\n",
    "\n",
    "###### 2.2.1 The  special method `__init__` \n",
    "\n",
    "The Special method names that start and end with `two` underscores <b style=\"color:blue\">__</b>. \n",
    "\n",
    "**Constructor  `__init__()`** : create instances of the class.\n",
    "\n",
    "* Whenever a class is <b>instantiated</b>, a call is made to the `__init__` method defined in that class.\n",
    "\n",
    "```python\n",
    "def __init__(self, radius=1.0):\n",
    "    \"\"\"Initializer with default radius of 1.0\"\"\"\n",
    "    self.radius = radius  # Create an instance variable radius\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.0"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c1=Circle()\n",
    "c1.radius"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "######  2.2.2 The  methods to get the area of this Circle \n",
    "\n",
    "```python\n",
    " def cal_area(self):\n",
    "        \"\"\"the area of this Circle instance\"\"\"\n",
    "        self.area=self.radius * self.radius * pi\n",
    "```            "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.3  Access any attribute\n",
    "\n",
    "The **“`.`”** operator is used to access any attribute\n",
    "\n",
    "* a data attribute of an object\n",
    "\n",
    "* a method of an object\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.1\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "13.854423602330987"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c1=Circle(2.1)\n",
    "print(c1.radius)\n",
    "c1.cal_area()\n",
    "c1.area"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.4 Add the Special Method `__str__` \n",
    "\n",
    "Add the Special Method `__str__`  to the class Circle"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from math import pi\n",
    " \n",
    "class Circle:  \n",
    "    \"\"\"A Circle instance models a circle with a radius\"\"\"\n",
    " \n",
    "    def __init__(self, radius=1.0):\n",
    "        \"\"\"Initializer with default radius of 1.0\"\"\"\n",
    "        self.radius = radius  # an instance variable radius\n",
    "        self.area=None  # init self.area=None\n",
    "   \n",
    "    def cal_area(self):\n",
    "        \"\"\"the area of this Circle instance\"\"\"\n",
    "        self.area= self.radius * self.radius * pi  \n",
    "    \n",
    "    def __str__(self):\n",
    "        \"\"\"Returns a string representation of  Circle\"\"\"\n",
    "        if self.area is not None:\n",
    "            result = \"The Circle: radius={:.3f},area={:.3f}\".format(self.radius,self.area)\n",
    "        else:\n",
    "            result =\"The Circle: radius={:.3f},area=None\".format(self.radius)\n",
    "        return  result  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "###### 2.4.1 the `print` command is used \n",
    "\n",
    "the `__str__` function associated with the object to be `printed` is <b>automatically invoked</b>."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The Circle: radius=2.100,area=None\n",
      "The Circle: radius=2.100,area=13.854\n"
     ]
    }
   ],
   "source": [
    "c1=Circle(2.1)\n",
    "print(c1)\n",
    "c1.cal_area()\n",
    "print(c1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###### 2.4.2  calling `str`\n",
    "\n",
    "the `__str__` function is automatically invoked to convert a instance of that class a string"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "str(c1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "c1.__str__()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###### 2.4.3 Build-in __str__ :List,dict,tuple"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "l=[1,2,3]\n",
    "print(l)\n",
    "str(l)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "l.__str__()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "d={'a':1,'b':2}\n",
    "print(d)\n",
    "str(d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "d.__str__()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "t=('a',1,'c')\n",
    "print(t)\n",
    "str(t)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "t.__str__()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3 Inheritance\n",
    "\n",
    "**Inheritance** provides a convenient mechanism for building **groups of `related` abstractions**\n",
    "\n",
    "It allows programmers to create <b>a type hierarchy</b> in which each type inherits attributes from the types above it in the hierarchy.\n",
    "\n",
    "\n",
    "### The class `Cylinder`\n",
    "\n",
    " We shall define a `Cylinder` class, as a subclass of `Circle`. \n",
    " \n",
    " The Cylinder class shall inherit attributes `radius` from the superclass `Circle`, and add its own attributes  and mathods \n",
    " \n",
    "The subclass Cylinder add **new** attributes: \n",
    "\n",
    "* **the class  variable**: `nextIdNum`, belongs to the class ` Cylinder`, rather than to instances of the class.\n",
    "* `idNum`： is initialized using <b>a class variable</b>, `nextIdNum`,\n",
    "* `heigh`\n",
    "* `volume`\n",
    "* `area`\n",
    "* `__circlearea`: Private Variable\n",
    "\n",
    "**the class variable**\n",
    "\n",
    "* belongs to the class\n",
    "\n",
    "* shared by all instance of the classs\n",
    "\n",
    "add **new** methods: \n",
    "\n",
    "* `cal_volume(self)`.\n",
    "\n",
    "override methods: \n",
    "\n",
    "* `cal_area(self)`:\n",
    "\n",
    "* `__str__(self)`:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Cylinder(Circle):\n",
    "    \"\"\"The Cylinder class is a subclass of Circle\"\"\"\n",
    "    \n",
    "    nextIdNum = 1 # identification number　- class\n",
    "   \n",
    "    def __init__(self, radius = 1.0, height = 1.0):\n",
    "        \"\"\"Initializer\"\"\"\n",
    "        super().__init__(radius)  # Invoke superclass' initializer (Python 3)\n",
    "        # Circle.__init__(self, radius)  Explicit superclass class\n",
    "        self.idNum = Cylinder.nextIdNum\n",
    "        Cylinder.nextIdNum += 1 # identification number\n",
    "        self.height = height\n",
    "        self.volume=None\n",
    "        self.__circlearea=None;\n",
    " \n",
    "    # Override\n",
    "    def cal_area(self):\n",
    "        \"\"\"Return the surface area the cylinder\"\"\"\n",
    "        self.__circlearea= self.radius * self.radius * pi  \n",
    "        self.area=2.0 * pi * self.radius * self.height+2.0*self.__circlearea\n",
    "  \n",
    "    def cal_volume(self):\n",
    "        \"\"\"Return the volume of the cylinder\"\"\"\n",
    "        self.volume=self.__circlearea * self.height  # Use superclass' cal_area()\n",
    "    \n",
    "    # Override\n",
    "    def __str__(self):\n",
    "        \"\"\"Self Description for print() and str()\"\"\"\n",
    "        # If __str__ is missing in the subclass, print() will invoke the superclass version!\n",
    "        result='Cylinder ID:{}\\n'.format(self.idNum)\n",
    "        result+='\\tradius={},height={}\\n'.format(self.radius, self.height)\n",
    "        result+='\\tarea={:<6.2f},volume={:<6.2f}'.format(self.area, self.volume)                                            \n",
    "        return result\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Cylinder ID:5\n",
      "\tradius=1.1,height=2.2\n",
      "\tarea=22.81 ,volume=8.36  \n"
     ]
    }
   ],
   "source": [
    "cy1 = Cylinder(radius=1.1, height=2.2)\n",
    "cy1.cal_area()   # Invoke overridden version\n",
    "cy1.cal_volume() # Invoke its method\n",
    "print(cy1)  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "22.8079626650619\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "'Cylinder' object has no attribute '__circlearea'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-15-fc3135a92608>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcy1\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0marea\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcy1\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__circlearea\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m: 'Cylinder' object has no attribute '__circlearea'"
     ]
    }
   ],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Cylinder ID:3\n",
      "\tradius=2.1,height=3.2\n",
      "\tarea=69.93 ,volume=44.33 \n"
     ]
    }
   ],
   "source": [
    "cy2 = Cylinder(2.1, 3.2)\n",
    "cy2.cal_area()   # Invoke overridden version\n",
    "cy2.cal_volume() # Invoke its method\n",
    "print(cy2)   "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Class variable"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4\n",
      "4\n",
      "4\n"
     ]
    }
   ],
   "source": [
    "print(Cylinder.nextIdNum)\n",
    "print(cy1.nextIdNum)\n",
    "print(cy2.nextIdNum)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Private Variables\n",
    "\n",
    "In Python, all attributes are \"public\" and are accessible by ALL.\n",
    "\n",
    "However, by convention:\n",
    "\n",
    "* Names begin with double underscores (`__`) and not end with double underscores are further hidden from direct access "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(cy1.area)  \n",
    "print(cy1.__circlearea)  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Further Reading: \n",
    "\n",
    "\n",
    "* [Python Object Oriented Programming (OOP)](http://www3.ntu.edu.sg/home/ehchua/programming/webprogramming/Python1a_OOP.html)\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.7"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "165px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  },
  "widgets": {
   "state": {},
   "version": "1.1.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
